﻿#region License

/* 
Copyright 2008 William C. Pierce 

Licensed under the Apache License, Version 2.0 (the "License"); 
you may not use this file except in compliance with the License. 
You may obtain a copy of the License at 

	http://www.apache.org/licenses/LICENSE-2.0 

Unless required by applicable law or agreed to in writing, software 
distributed under the License is distributed on an "AS IS" BASIS, 
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
See the License for the specific language governing permissions and 
limitations under the License. 
*/

#endregion

using System.Collections.Generic;
using NHibernate.Mapping;

namespace NHibernate.Proxy.Generator
{
	using System;
	using System.CodeDom.Compiler;
	using System.Collections;
	using System.IO;
	using System.Reflection;
	using System.Text;
	using Castle.ActiveRecord;
	using Castle.DynamicProxy;
	using Cfg;
	using ILMerging;
	using Microsoft.CSharp;

	public class Program
	{
		public static readonly ProxyGenerator ProxyGenerator = new ProxyGenerator( new DefaultProxyBuilder( new ModuleScope( true ) ) );
		public static readonly IDictionary Proxies = Hashtable.Synchronized( new Hashtable() );

		private static void Main(string[] args)
		{
			ApplicationOptions options = new ApplicationOptions();
			if( Parser.ParseArgumentsWithUsage( args, options ) == false )
			{
				System.Environment.Exit( 2 );
			}

			string inputFilePath = Path.GetFullPath( options.InputFile );
			string inputDirectory = Path.GetDirectoryName( inputFilePath );
			AppDomain.CurrentDomain.AssemblyResolve += delegate(object sender, ResolveEventArgs e)
				{
					string asmFileName = e.Name.Split( ',' )[0];
					string exeFileName = Path.Combine( inputDirectory, asmFileName + ".exe" );
					if( File.Exists( exeFileName ) )
					{
						return Assembly.LoadFile( exeFileName );
					}
					string dllFileName = Path.Combine( inputDirectory, asmFileName + ".dll" );
					if( File.Exists( dllFileName ) )
					{
						return Assembly.LoadFile( dllFileName );
					}
					return null;
				};

			Assembly inputAssembly = Assembly.LoadFile( inputFilePath );

			IDictionary nhibernateProperties = new Hashtable();
			nhibernateProperties["cache.provider_class"] = "NHibernate.Cache.HashtableCacheProvider";
			nhibernateProperties["dialect"] = "NHibernate.Dialect.MsSql2000Dialect";
			nhibernateProperties["proxyfactory.factory_class"] = "NHibernate.Proxy.Generator.GeneratorProxyFactoryFactory, NPG";
			nhibernateProperties["default-lazy"] = options.DefaultLazy;

			ActiveRecordConfigurationSource activeRecordConfiguration = new ActiveRecordConfigurationSource();
			activeRecordConfiguration.Add( nhibernateProperties );
			ActiveRecordStarter.Initialize( inputAssembly, activeRecordConfiguration );
			Configuration nhibernateConfiguration = ActiveRecordMediator.GetSessionFactoryHolder().GetConfiguration( typeof( ActiveRecordBase ) );
			nhibernateConfiguration.AddAssembly( inputAssembly );
			nhibernateConfiguration.BuildSessionFactory();

			ProxyGenerator.ProxyBuilder.ModuleScope.SaveAssembly();

			StringBuilder proxies = new StringBuilder();
			foreach( DictionaryEntry entry in Proxies )
			{
				proxies.AppendFormat( "\t\t_proxies[\"{0}\"] = typeof({1});\r\n", entry.Key, ((Type)entry.Value).Name );
			}

			string source;
			using( Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream( "NHibernate.Proxy.Generator.StaticProxyFactory.cs" ) )
			{
				using( TextReader reader = new StreamReader( stream ) )
				{
					source = reader.ReadToEnd();
				}
				source = source.Replace( "//", string.Empty );
				source = source.Replace( "{VERSION}", inputAssembly.GetName().Version.ToString() );
				source = source.Replace( "{PROXIES}", proxies.ToString() );
			}

			CompilerParameters parameters = new CompilerParameters();
			parameters.OutputAssembly = "StaticProxyFactory.dll";
			parameters.IncludeDebugInformation = true;
			parameters.GenerateInMemory = false;
			parameters.WarningLevel = 4;
			parameters.TreatWarningsAsErrors = true;
			parameters.CompilerOptions = "/optimize";

			AssemblyName proxyAssemblyName = new AssemblyName( "DynamicProxyGenAssembly2" );
			proxyAssemblyName.CodeBase = "CastleDynProxy2.dll";

			List<Assembly> references = new List<Assembly>();
			references.Add( Assembly.Load( "NHibernate" ) );  
			references.Add( Assembly.Load( "NHibernate" ) );
			references.Add( Assembly.Load( "Iesi.Collections" ) );
			references.Add( Assembly.Load( "log4net" ) );
			references.Add( Assembly.Load( "Castle.Core" ) );
			references.Add( Assembly.Load( proxyAssemblyName ) );
			foreach( PersistentClass cls in nhibernateConfiguration.ClassMappings )
			{
				if( !references.Contains( cls.MappedClass.Assembly ) )
				{
					references.Add( cls.MappedClass.Assembly ); 
				}
			}

			foreach( Assembly assembly in references )
			{
				parameters.ReferencedAssemblies.Add( assembly.Location );
			}

			CSharpCodeProvider compiler = new CSharpCodeProvider();
			CompilerResults result = compiler.CompileAssemblyFromSource( parameters, source );
			if( result.Errors.HasErrors )
			{
				StringBuilder errors = new StringBuilder();
				foreach( CompilerError error in result.Errors )
				{
					errors.AppendLine( error.ToString() );
				}
				throw new Exception( errors.ToString() );
			}

			ILMerge merger = new ILMerge();
			merger.SetSearchDirectories( new string[] {inputDirectory} );
			merger.SetInputAssemblies( new string[] {"StaticProxyFactory.dll", "CastleDynProxy2.dll"} );
			merger.OutputFile = options.OutputFile;
			merger.Merge();
		}
	}
}